package com.crimsec.utils.dinfo.impl.xml;

import com.crimsec.utils.dinfo.IRenderer;
import com.crimsec.utils.dinfo.Utils;
import com.crimsec.utils.dinfo.impl.AbstractRenderer;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.SortedMap;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.util.JAXBSource;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class XMLRenderer extends AbstractRenderer implements IRenderer {

    //   private Utils utils;
    private boolean useRawXML = true;
    private String xslStyle = "", baseURI="", encoding = "";

    public String getEncoding() {
        return encoding;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    public boolean isUseRawXML() {
        return useRawXML;
    }

    public void setUseRawXML(boolean useRawXML) {
        this.useRawXML = useRawXML;
    }

    public String getXslStype() {
        return xslStyle;
    }

    public void setXslStype(String xslStype) {
        this.xslStyle = xslStype;
    }

    public String getBaseURI() {
        return baseURI;
    }

    public void setBaseURI(String baseURI) {
        this.baseURI = baseURI;
    }

    @Override
    public void setup(Utils utils) {
        if (utils.checkProp("xmlrenderer.xml.raw")) {
            useRawXML = Boolean.valueOf(
                    utils.getValue("xmlrenderer.xml.raw"));
        }
        if (utils.checkProp("xmlrenderer.encoding")) {
            encoding = utils.getValue("xmlrenderer.encoding");
        } else {
            encoding = System.getProperty("file.encoding");
        }
        if (utils.checkProp("xmlrenderer.xsl.style")) {
            xslStyle = utils.getValue("xmlrenderer.xsl.style");
        }
        if (utils.checkProp("xmlrenderer.baseUri")) {
            baseURI = utils.getValue("xmlrenderer.baseUri");
        }
        this.setupIt = true;
    }

    public void render(SortedMap<Long, File> files,
            File init_dir,
            String name, int count) throws IOException {

        check(files, init_dir, name);

        NewsIndex ni = new NewsIndex();
        ni.setBaseUri(URI.create(this.baseURI));
        ni.setEncoding(this.encoding);
        ni.setNewsItems(createNewsItems(files, ni.getBaseUri(), count));
        try {
            JAXBContext jbCtx = JAXBContext.newInstance(NewsIndex.class, NewsItem.class);
            if (useRawXML) {
                renderRaw(jbCtx, ni, new File(init_dir, name));
            } else {
                renderTransform(jbCtx, ni, new File(init_dir, name));
            }


        } catch (Exception ex) {
            throw new IOException("can't render index file,  exception occurs " + ex.getMessage(), ex);
        }
    }

    private void renderRaw(JAXBContext jbCtx, Object jbE, File to) throws Exception {
        Marshaller marsh = jbCtx.createMarshaller();
        marsh.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marsh.marshal(jbE, to);
    }

    private void renderTransform(JAXBContext jbCtx, NewsIndex jbE, File to) throws Exception {
        JAXBSource jbSrc = new JAXBSource(jbCtx, jbE);
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer t = tf.newTransformer(new StreamSource(Utils.openStream(xslStyle, this)));
        t.setOutputProperty(OutputKeys.INDENT, "yes");
        t.setOutputProperty(OutputKeys.ENCODING, jbE.getEncoding());

        t.transform(jbSrc, new StreamResult(to));
    }

    public Collection<NewsItem> createNewsItems(SortedMap<Long, File> files,
            URI baseURI, int count) throws IOException {

        SortedMap<Long, File> psm = getHeadMapForCount(files, count);
        ArrayList<NewsItem> result = new ArrayList<NewsItem>(files.size());
        for (File f : psm.values()) {
            NewsItem i = new NewsItem();
            try {
                i.setCtime(new Date(f.lastModified()));
            } catch (Exception e) {
            }

            i.setName(f.getName());
            i.setUri(createURI(baseURI, f));
            result.add(i);
        }
        return result;
    }

    //TODO: More reasonable check here
    public static URI createURI(URI baseUri, File f) {
        if (baseUri != null) {
            return baseUri.resolve(f.getName());
        } else {
            return URI.create(f.getName());
        }
    }

    public XMLRenderer() {
    }
}
